#include <iostream>
#include <fstream>
#include <sstream>
#include <cstdlib>
#include <string>
#include <algorithm>
#include <vector>
#include <set>
#include <map>
#include <queue>
#include <list>
#include <cassert>
#include <utility>
#include <math.h>
#include <string.h>

// pour la seconde partie du projet
#include "expression_rationnelle.hpp"
#include "parser.hpp"

using namespace std;


////////////////////////////////////////////////////////////////////////////////

const unsigned int ASCII_A = 97;
const unsigned int ASCII_Z = ASCII_A + 26;
const bool         DEBUG = false;

typedef size_t                            etat_t;
typedef unsigned char                     symb_t;
typedef set< etat_t >                     etatset_t;
typedef vector< vector< etatset_t > >     trans_t;
typedef vector< etatset_t >               epsilon_t;
typedef map< etatset_t, etat_t >          map_t;


////////////////////////////////////////////////////////////////////////////////

struct sAutoNDE
{
    // caractéristiques
    size_t nb_etats;
    size_t nb_symbs;
    size_t nb_finaux;

    etat_t initial;
    // état initial

    etatset_t finaux;
    // états finaux : finaux_t peut être un int*, un tableau dynamique comme vector<int>
    // ou une autre structure de donnée de votre choix.

    trans_t trans;
    // matrice de transit : trans_t peut être un int***, une structure dynamique 3D comme vector< vector< set<int> > >
    // ou une autre structure de donnée de votre choix.

    epsilon_t epsilon;
    // transits spontanées : epsilon_t peut être un int**, une structure dynamique 2D comme vector< set<int> >
    // ou une autre structure de donnée de votre choix.
};

////////////////////////////////////////////////////////////////////////////////

ostream& operator<<(ostream& out, const sAutoNDE& at);

bool FromFile(sAutoNDE& at, string path)
{
    ifstream myfile(path.c_str(), ios::in);
    //! un flux d'entree obtenu à partir du nom du fichier

    string line;
    //! une ligne lue dans le fichier avec getline(myfile,line);

    istringstream iss;
    //! flux associé à la chaine, pour lire morceau par morceau avec >> (comme cin)

    etat_t s(0), t(0);
    //! deux états temporaires

    symb_t a(0);
    //! un symbole temporaire

    ostream out(NULL);



    if (myfile.is_open())
    {
        //! la première ligne donne 'nb_etats nb_symbs nb_finaux'
        do
        {
            getline(myfile,line);
        }
        while (line.empty() || line[0]=='#');
        // on autorise les lignes de commentaires : celles qui commencent par '#'


        iss.str(line);
        if((iss >> at.nb_etats).fail() || (iss >> at.nb_symbs).fail() || (iss >> at.nb_finaux).fail())
        {
            return false;
            // la deuxième ligne donne l'état initial
        }

        do
        {
            getline (myfile,line);
        }
        while (line.empty() || line[0]=='#');


        iss.clear();
        iss.str(line);
        if((iss >> at.initial).fail())
        {
            return -1;
        }


        // les autres lignes donnent les états finaux
        for(size_t i = 0; i < at.nb_finaux; i++)
        {
            do
            {
                getline (myfile,line);
            }
            while (line.empty() || line[0]=='#');

            iss.clear();
            iss.str(line);

            if((iss >> s).fail())
            {
                continue;
//!        cerr << "s= " << s << endl;
            }

            at.finaux.insert(s);
        }

        // on alloue les vectors à la taille connue à l'avance pour éviter les resize dynamiques
        at.epsilon.resize(at.nb_etats);
        at.trans.resize(at.nb_etats);

        for(size_t i=0; i<at.nb_etats; ++i)
        {
            at.trans[i].resize(at.nb_symbs);
        }


        // lecture de la relation de transit
        while(myfile.good())
        {
            line.clear();
            getline (myfile,line);
            if (line.empty() && line[0]=='#')
            {
                continue;
            }

            iss.clear();
            iss.str(line);

            // si une des trois lectures echoue, on passe à la suite
            if((iss >> s).fail() || (iss >> a).fail() || (iss >> t).fail() || (a< ASCII_A ) || (a> ASCII_Z ))
            {
                continue;
            }


            //test espilon ou non
            if ((a-ASCII_A) >= at.nb_symbs)
            {
                cerr << "s=" << s<< ", (e), t=" << t << endl;
                at.epsilon[s].insert(t);
            }
            else
            {
                cerr << "s=" << s<< ", a=" << a-ASCII_A << ", t=" << t << endl;
                at.trans[s][a-ASCII_A].insert(t);
            }
        }
        out<<operator<<(out,at)<<endl;
        myfile.close();
        return true;
    }
    return false;
    // on ne peut pas ouvrir le fichier
}


// -----------------------------------------------------------------------------
// Fonctions à compléter pour la première partie du projet
// -----------------------------------------------------------------------------


////////////////////////////////////////////////////////////////////////////////

bool ContientFinal(const sAutoNDE& at,const etatset_t& e)
{
    // Double boucle sur e et sur les états finaux de at pour vérifier un élément de e est dans les états finaux
    for(etatset_t::const_iterator i=at.finaux.begin(); i!=at.finaux.end(); i++)
    {
        for(etatset_t::const_iterator j=e.begin(); j!=e.end(); j++)
        {
            if(*i==*j)
            {
                return true;
            }
        }
    }
    return false;
}

////////////////////////////////////////////////////////////////////////////////

bool EstDeterministe(const sAutoNDE& at)

{
    unsigned int i,j;
    // Si l'automate contient des epsilon transits alors il n'est pas déterministe.
    for(epsilon_t::const_iterator it=at.epsilon.begin(); it!=at.epsilon.end(); it++)
    {
        if(!(*it).empty())
            return false;
    }
    // Double boucle dans les vectors imbriqués de at.trans.
    //Si une case contient plus d'un élément dans l'ensemble à partir d'un état et d'une transit fixés
    //alors l'automate n'est pas déterministe
    for(i=0; i<at.trans.size(); i++)
    {
        for(j=0; j<at.trans[i].size(); j++)
        {
            if (at.trans[i][j].size()>1)
            {
                return false;
            }
        }
    }
    return true;
}

////////////////////////////////////////////////////////////////////////////////

void Fermeture(const sAutoNDE& at, etatset_t& e)
{
    // Cette fonction clot l'ensemble d'états E={e_0, e_1, ... ,e_n} passé en
    // paramètre avec les epsilon transits

    // 2 etatset de travail
    etatset_t temp1, temp2;
    // Copie de e dans un ensemble d'état temporair pour pouvoir ne pas avoir e en entré et en sortie,
    // ce qui pose des problèmes quant à l'arret de la fonction
    for(etatset_t::iterator it=e.begin(); it!=e.end(); it++)
        temp1.insert(*it);
    while(!temp1.empty())
    {
        for(etatset_t::iterator it=temp1.begin(); it!=temp1.end(); it++)
        {
            if(!at.epsilon[*it].empty())
            {
                for(etatset_t::iterator it2=at.epsilon[*it].begin(); it2!=at.epsilon[*it].end(); it2++)
                {
                    e.insert(*it2);
                    temp2.insert(*it2);
                }
            }
        }
        temp1 = temp2;
        temp2.clear();
    }
}

////////////////////////////////////////////////////////////////////////////////

etatset_t Delta(const sAutoNDE& at, const etatset_t& e, symb_t c)
{
    // définir cette fonction en utilisant Fermeture
    // Variable à renvoyer
    etatset_t etat_etat_courant;

    // On parcours e et si le symbole c mène quelque part à partir d'un état de e, on met à jour l'etat etat_courant
    for(etatset_t::iterator it=e.begin(); it!=e.end(); it++)
    {
        if(!at.trans[*it][c-ASCII_A].empty())
            etat_etat_courant.insert(at.trans[*it][c-ASCII_A].begin(),at.trans[*it][c-ASCII_A].end());
    }

    // On clot etat_etat_courant
    Fermeture(at,etat_etat_courant);

    return etat_etat_courant;

}

////////////////////////////////////////////////////////////////////////////////

bool Accept(const sAutoNDE& at, string str) //TESTER str si est dans mot acceptable
{
    // Déclaration d'un ensemble d'état pour stocker les états etat_courants
    etatset_t etat_etat_courant;

    // Mise en place de l'état initial de at dans les états etat_courants
    etat_etat_courant.insert(at.initial);
    Fermeture(at,etat_etat_courant);

    // Lecture symbole par symbole du mot en paramètre.
    //Construction des états au fur et à mesure de la lecture du mot
    for(unsigned i=0; i<str.size(); i++)
        etat_etat_courant = Delta(at,etat_etat_courant,str[i]);

    // Si un des états d'état_etat_courant se trouve dans at alors le mot est accepté
    return ContientFinal(at,etat_etat_courant);
}

////////////////////////////////////////////////////////////////////////////////

ostream& operator<<(ostream& out, const sAutoNDE& at)
{
    int i,j;
    /* Affichage du nombre d'état, de symboles et d'états finaux (première ligne) et de l'état initial (seconde ligne) */
    out << at.nb_etats << " " << at.nb_symbs << " " << at.finaux.size() << endl;
    out << at.initial << endl;

    /* Affichage des états finaux */
    for(etatset_t::iterator it=at.finaux.begin(); it!=at.finaux.end(); it++)
        out << *it << endl;

    /* Double boucle pour afficher la table des epsilon  */
    for(epsilon_t::const_iterator it=at.epsilon.begin(); it!=at.epsilon.end(); it++)
    {
        i=it-at.epsilon.begin();
        for(etatset_t::iterator it1=(*it).begin(); it1!=(*it).end(); it1++)
        {
            out<<i<<" e "<<*it1<<endl;
        }
    }

    /* Triple boucle pour afficher la table des transits  */
    for(trans_t::const_iterator it=at.trans.begin(); it!=at.trans.end(); it++)
    {
        i=it-at.trans.begin();
        for(vector<etatset_t>::const_iterator it1=(*it).begin(); it1!=(*it).end(); it1++)
        {
            j=it1-at.trans[i].begin();
            for(etatset_t::iterator it2=(*it1).begin(); it2!=(*it1).end(); it2++)
            {
                out<<i<<" "<<(char)(j+ASCII_A)<<" "<<*it2<<endl;
            }
        }
    }
    return out;
}


////////////////////////////////////////////////////////////////////////////////




sAutoNDE Determinize(const sAutoNDE& at)
{

    sAutoNDE res;
    etatset_t etat;
    epsilon_t epsilon_fermeture;
    etatset_t initial,transit;
    map_t mymap;

    res.nb_symbs = at.nb_symbs;

    //Construction de la fermeture de chaque etat + tableau de vector
    //d'etat set contenant les e-fermetures pour chaqu'un etats
    epsilon_fermeture.resize(at.nb_etats);

    for(unsigned int it = 0; it<at.nb_etats; it++)
    {
        etat.insert(it);
        Fermeture(at,etat);
        epsilon_fermeture[it].insert(etat.begin(),etat.end());
        etat.clear();
    }

    //On initialise l'etat initiale du nouvel automate comme at
    res.initial = 0;
    res.nb_finaux = 0;

    //l'epsilon_fermeture de l'etat initial est le premier etat de l'automate deterministe
    initial = epsilon_fermeture[at.initial];

    //On regarde si initial est un etat final
    if(ContientFinal(at,initial))
    {
        res.finaux.insert(0);
    }

    //On initialise le premier etat du nouvel automate
    mymap.insert(mymap.end(),pair<etatset_t,etat_t>(initial,0));

    //On reserve de la memoire pour trans
    res.epsilon.resize(at.nb_etats*at.nb_etats);

    //On reserve de la memoire pour epsilon
    res.trans.resize(at.nb_etats*at.nb_etats);

    for(size_t p=0; p<(int)at.nb_etats*at.nb_etats; ++p)
    {
        res.trans[p].resize(at.nb_symbs);
    }

    queue<etatset_t> etats_file;
    etats_file.push(initial);

    etatset_t etat_courant;
    pair<map_t::iterator,bool> ret;


    /*etats_file est la liste des etats qui reste a traiter
     *Comme l'algo du cours, elle se remplie seulement avec des etats qui n'ont
     *pas encore ete traites*/

    while(!etats_file.empty())
    {

        //on récupère le premier élément de la file
        etat_courant = etats_file.front();
        //on defile le premier élément
        etats_file.pop();

        for(unsigned int i = 0; i<res.nb_symbs; i++)
        {
            transit = Delta(at,etat_courant,(char)i+ASCII_A);

            //la map ne prend encompte les doublons
            ret = mymap.insert(pair<etatset_t,etat_t>(transit,mymap.size()));

            //Si la transition n'existe pas dans la map
            if(ret.second == true)
            {
                //On l'enfile
                etats_file.push(transit);

                if(ContientFinal(at,transit))
                {
                    res.finaux.insert(mymap.find(transit)->second);
                }
            }
            res.trans[mymap.find(etat_courant)->second][i].insert(mymap.find(transit)->second);
        }
    }

    //On affecte les valeurs maintenant que le traitement est terminé
    res.nb_finaux = res.finaux.size();
    res.nb_etats = mymap.size();

    return res;

}

// -----------------------------------------------------------------------------
// Fonctions à compléter pour la seconde partie du projet
// -----------------------------------------------------------------------------

////////////////////////////////////////////////////////////////////////////////

bool ToGraph(sAutoNDE& at, string path)
{

    ofstream fichier(path.c_str(), ios::out | ios::trunc);
    //si le fichier engendre une erreur on retourne faux
    if(!fichier)
    {
        return false;
    }

    //On respecte le format du fichier
    fichier<<"digraph finite_state_machine {"<<endl;
    fichier<<"rankdir=LR;"<<endl;
    fichier<<"size=\"10,10\""<<endl<<endl;
    fichier<<"node [shape = doublecircle];";

    //On écrit tous les états finaux
    for(set<size_t>::iterator it=at.finaux.begin(); it!=at.finaux.end(); it++)
    {
        fichier<<*it<<" ";
    }

    fichier<<endl;
    // Le point et la fleche représententent l'état initial
    fichier<<"node [shape = point]; q;"<<endl;
    // Les cercles resprésentent les états
    fichier<<"node [shape = circle];"<<endl;
    // Cas de l'état initial
    fichier<<"q->"<<at.initial<<endl;

    // On traite le cas des transitions non spontannées
    for(size_t i=0; i<at.nb_etats; i++)
    {
        for(size_t j=0; j<at.nb_symbs; j++)
        {
            if(at.trans[i][j].size()>0)
            {
                for(set<size_t>::iterator it=at.trans[i][j].begin(); it!=at.trans[i][j].end(); it++)
                {
                    fichier<<i<<"->"<<*it<<"[label = \""<<(char)(j+ASCII_A)<<"\"];"<<endl;
                }
            }
        }
    }

    fichier<<endl;

    // On traite le cas des transitions spontannées
    for(size_t i=0; i<at.epsilon.size(); i++)
    {
        if(at.epsilon[i].size()>0)
        {
            for(set<size_t>::iterator it=at.epsilon[i].begin(); it!=at.epsilon[i].end(); it++)
            {
                fichier<<i<<"->"<<*it<<"[label = \"ε\"];"<<endl;
            }
        }

    }

    fichier<<"}"<<endl;
    fichier.close();
    return true;
}


////////////////////////////////////////////////////////////////////////////////

// fonction outil : on garde x, et on "ajoute" trans et epsilon de y
// en renommant ses états, id est en décallant les indices des états de y
// de x.nb_etats
sAutoNDE Append(const sAutoNDE& x, const sAutoNDE& y)
{
    assert(x.nb_symbs == y.nb_symbs);
    sAutoNDE r;

    // On copie l'automate x dans celui de sortie
    r = x;
    // On assigne l'état initial de x
    r.initial = x.initial;
    // On ajoute à l'automate r les états de celui de y
    r.trans.resize(r.nb_etats);
    r.nb_etats = x.nb_etats + y.nb_etats;

    // On ajoute trans de y en décalant les états
    for (size_t i = 0; i < y.nb_etats; i++)
    {
        r.trans[i + x.nb_etats].resize(x.nb_symbs);
        for (size_t j = 0; j < x.nb_symbs; j++)
        {
            for (set<size_t>::iterator it = y.trans[i][j].begin(); it != y.trans[i][j].end(); it++)
            {
                r.trans[i + x.nb_etats][j].insert((*it + x.nb_etats));
            }
        }
    }

    //On redimensionne epsilon pour accueillir celui de y
    r.epsilon.resize(x.epsilon.size() + y.epsilon.size());

    // On ajoute epsilon de y en décalant les états
    for (unsigned int i = 0; i < y.epsilon.size(); i++)
    {
        for (set<size_t>::iterator it = y.epsilon[i].begin(); it != y.epsilon[i].end(); it++)
        {
            r.epsilon[i + x.nb_etats].insert((*it + x.nb_etats));
        }
    }

    // On vide les états finaux
    r.finaux.clear();
    return r;
}

////////////////////////////////////////////////////////////////////////////////

sAutoNDE Union(const sAutoNDE& x, const sAutoNDE& y)
{
    assert(x.nb_symbs == y.nb_symbs);
    sAutoNDE r = Append(x, y);

    //On ajoute un état initial global à nb_etats
    //et on initialise nb_finaux à 0
    r.nb_etats++;
    r.nb_finaux = 0;
    r.trans.resize(r.nb_etats);

    //On ajuste la taille de trans
    r.trans[r.nb_etats - 1].resize(r.nb_symbs);

    //Le nouvel état initial effectue des e-transitions
    //sur les états initiaux de x et y
    r.epsilon.resize(r.nb_etats);
    r.epsilon[r.nb_etats - 1].insert(x.initial);
    r.epsilon[r.nb_etats - 1].insert(y.initial + x.nb_etats);
    r.initial = r.nb_etats - 1;

    //On met à jour les états finaux en ajoutant ceux de l'automate x puis ceux de y
    for (set<size_t>::iterator it = x.finaux.begin(); it != x.finaux.end(); it++)
    {
        r.finaux.insert(*it);
        r.nb_finaux++;
    }
    for (set<size_t>::iterator it = y.finaux.begin(); it != y.finaux.end(); it++)
    {
        r.finaux.insert(*it + x.nb_etats);
        r.nb_finaux++;
    }

    return r;
}

////////////////////////////////////////////////////////////////////////////////


sAutoNDE Concat(const sAutoNDE& x, const sAutoNDE& y)
{
    assert(x.nb_symbs == y.nb_symbs);
    sAutoNDE r = Append(x, y);
    r.nb_finaux=0;

    //On ajoute les états finaux de l'automate y modifiés par la fonction append
    //et on renomme les états de y pour construire le nouveau trans

    for (set<size_t>::iterator it = y.finaux.begin(); it != y.finaux.end(); it++)
    {
        r.finaux.insert(*it + x.nb_etats);
        r.nb_finaux++;
    }

    for (set<size_t>::iterator it = x.finaux.begin(); it != x.finaux.end(); it++)
    {
        r.epsilon[*it].insert(y.initial+x.nb_etats);
    }

    return r;
}

////////////////////////////////////////////////////////////////////////////////

sAutoNDE Complement(const sAutoNDE& x)
{
    //Automate de travail
    sAutoNDE work;
    //On commence par déterminiser l'automate si ce n'est pas déjà la cas
    work = Determinize(x);

    size_t nb_etat_final = 0;
    etatset_t etats_finaux;
    etatset_t temp;

    //On prend le complémentaire de tous les états finaux
    for(size_t i=0; i<work.nb_etats; ++i)
    {
        temp.insert((etat_t)i);
        if(!ContientFinal(work,temp))
        {
            etats_finaux.insert((etat_t)i);
            nb_etat_final++;
        }
        temp.clear();
    }
    work.nb_finaux = nb_etat_final;
    work.finaux = etats_finaux;

    return work;
}



////////////////////////////////////////////////////////////////////////////////

sAutoNDE Kleene(const sAutoNDE& x)
{
    sAutoNDE work = x;


    work.nb_etats++;
    work.initial = work.nb_etats - 1;

    // On ajoute un epsilon vers x.initial
    for (set<size_t>::iterator it = x.finaux.begin(); it != x.finaux.end(); it++)
        work.epsilon[*it].insert(x.initial);

    // On utilise un état qui nous servira d'état initial ayant un epsilon vers l'ancien état initial
    work.epsilon.resize(work.nb_etats + 1);
    work.epsilon[work.nb_etats].insert(x.initial);

    // On évite les dépassements de mémoire
    work.trans.resize(work.nb_etats);

    for (size_t i = 0; i < work.nb_etats; i++)
        work.trans[i].resize(x.nb_symbs);

    // Le nouvel état est final
    work.finaux.insert(work.nb_etats - 1);
    work.nb_finaux++;

    return work;
}

////////////////////////////////////////////////////////////////////////////////


sAutoNDE Intersection(const sAutoNDE& x, const sAutoNDE& y)
{
    sAutoNDE z;

    /* Par définition, l'intersection le complément
    de l'union des 2 automates
    */
    z = Complement(Union(Complement(x),Complement(y)));
    return z;
}

////////////////////////////////////////////////////////////////////////////////

sAutoNDE working_function(sExpressionRationnelle er)
{
    sAutoNDE res,r1,r2;

    //On alloue de l'espace memoire pour le trans et epsilon
    res.epsilon.resize(pow(5,5));
    res.trans.resize(pow(5,5));
    for(size_t p=0; p<(int)pow(5,5); ++p)
    {
        res.trans[p].resize(5);
    }

    char *s;

    switch(er->op)
    {
    case o_variable:

        //On initialise le nb de symb a 5 pour ne pas avoir de problème de taille dans le cas où
        //on doive transformer une expression rationnelle de 5 symboles
        //et pour respecter les contraites des fonctions concat, Union et Kleen
        res.nb_symbs = 5;
        res.initial = 0;

        s = const_cast<char*>(er->nom->c_str());

        if(strcmp(s,"e") == 0)
        {
            res.epsilon[0].insert(1);
        }
        else
        {
            res.trans[0][s[0] - ASCII_A].insert(1);
        }

        res.nb_finaux = 1;
        res.finaux.insert(1);

        res.nb_etats = 2;

        return res;

    case o_etoile:

        r1 = working_function(er->arg);
        res = Kleene(r1);

        return res;
    case o_concat:

        r1 = working_function(er->arg1);
        r2 = working_function(er->arg2);

        res = Concat(r1,r2);

        return res;
    case o_ou:
        r1 = working_function(er->arg1);
        r2 = working_function(er->arg2);

        res = Union(r1,r2);

        return res;
    }

    return res;
}

sAutoNDE ExpressionRationnelle2Automate(string expr)
{
    sAutoNDE res;
    char * chaine;
    char c;

    sExpressionRationnelle e_rationnelle = lit_expression_rationnelle(expr);

    res = working_function(e_rationnelle);
    chaine = const_cast<char*>(expr.c_str());

    c = 0;
    //On met a jour le nb symboles
    for(size_t i = 0; i<expr.size(); i++)
    {
        if((size_t)chaine[i] >= ASCII_A && (size_t)chaine[i] <= ASCII_Z && chaine[i] > c)
        {
            c = chaine[i];
        }
    }

    res.nb_symbs = c - ASCII_A + 1;

    return res;
}


////////////////////////////////////////////////////////////////////////////////

void Help(ostream& out, char *s)
{
    out << "Utilisation du programme " << s << " :" << endl ;
    out << "-acc ou -accept Input Word:\n\t détermine si le mot Word est accepté" << endl;
    out << "-det ou -determinize Input :\n\t déterminise Input" << endl;
    out << "-cup ou -union Input1 Input2 :\n\t calcule l'union" << endl;
    out << "-cat ou -concat Input1 Input2 :\n\t calcul la concaténation" << endl;
    out << "-star ou -kleene Input :\n\t calcul de A*" << endl;
    out << "-bar ou -complement Input :\n\t calcul du complément" << endl;
    out << "-cap ou -intersection Input1 Input2 :\n\t calcul de l'intersection" << endl;
    out << "-expr2aut ou expressionrationnelle2automate ExpressionRationnelle :\n\t calcul de l'automate correspondant à l'expression rationnelle" << endl;
    out << "-nop ou -no_operation Input :\n\t ne rien faire de particulier" << endl;

    out << "-o ou -output Output :\n\t écrire le résultat dans le fichier Output, afficher sur STDOUT si non spécifié" << endl;
    out << "-g ou -graphe :\n\t l'output est au format dot/graphiz" << endl  << endl;

    out << "Exemple '" << s << " -determinize auto.txt -output determin.txt'" << endl;
}



////////////////////////////////////////////////////////////////////////////////

int main(int argc, char* argv[] )
{
    if(argc < 3)
    {
        Help(cout, argv[0]);
        return EXIT_FAILURE;
    }

    int pos;
    int act=-1;                 // pos et act pour savoir quelle action effectuer
    int nb_files = 0;           // nombre de fichiers en entrée
    string str, in1, in2, out, acc, expr;
    // chaines pour (resp.) tampon; fichier d'entrée Input1; fichier d'entrée Input2;
    // fichier de sortie et chaine dont l'acceptation est à tester
    bool toFile=false, graphMode=false;     // sortie STDOUT ou fichier ? Si fichier, format graphviz ?

    // options acceptées
    const size_t NBOPT = 11;
    string aLN[] = {"accept", "determinize", "union", "concat", "kleene", "complement", "intersection", "expressionrationnelle2automate", "no_operation", "output", "graph"};
    string aSN[] = {"acc", "det", "cup", "cat", "star", "bar", "cap", "expr2aut", "nop", "o", "g"};

    // on essaie de "parser" chaque option de la ligne de commande
    for(int i=1; i<argc; ++i)
    {
        if (DEBUG) cerr << "argv[" << i << "] = '" << argv[i] << "'" << endl;
        str = argv[i];
        pos = -1;
        string* pL = find(aLN, aLN+NBOPT, str.substr(1));
        string* pS = find(aSN, aSN+NBOPT, str.substr(1));

        if(pL!=aLN+NBOPT)
            pos = pL - aLN;
        if(pS!=aSN+NBOPT)
            pos = pS - aSN;

        if(pos != -1)
        {
            // (pos != -1) <=> on a trouvé une option longue ou courte
            if (DEBUG) cerr << "Key found (" << pos << ") : " << str << endl;
            switch (pos)
            {
            case 0: //acc
                in1 = argv[++i];
                acc = argv[++i];
                nb_files = 1;
                break;
            case 1: //det
                in1 = argv[++i];
                nb_files = 1;
                break;
            case 2: //cup
                in1 = argv[++i];
                in2 = argv[++i];
                nb_files = 2;
                break;
            case 3: //cat
                in1 = argv[++i];
                in2 = argv[++i];
                nb_files = 2;
                break;
            case 4: //star
                in1 = argv[++i];
                nb_files = 1;
                break;
            case 5: //bar
                in1 = argv[++i];
                nb_files = 1;
                break;
            case 6: //cap
                in1 = argv[++i];
                in2 = argv[++i];
                nb_files = 2;
                break;
            case 7: //expr2aut
                expr = argv[++i];
                nb_files = 0;
                break;
            case 8: //nop
                in1 = argv[++i];
                nb_files = 1;
                break;
            case 9: //o
                toFile = true;
                out = argv[++i];
                break;
            case 10: //g
                graphMode = true;
                break;
            default:
                return EXIT_FAILURE;
            }
        }
        else
        {
            cerr << "Option inconnue "<< str << endl;
            return EXIT_FAILURE;
        }

        if(pos<9)
        {
            if(act > -1)
            {
                cerr << "Plusieurs actions spécififées"<< endl;
                return EXIT_FAILURE;
            }
            else
                act = pos;
        }
    }

    if (act == -1)
    {
        cerr << "Pas d'action spécififée"<< endl;
        return EXIT_FAILURE;
    }

    /* Les options sont OK, on va essayer de lire le(s) automate(s) at1 (et at2)
    et effectuer l'action spécifiée. Atr stockera le résultat*/

    sAutoNDE at1, at2, atr;

    if ((nb_files == 1 or nb_files == 2) and !FromFile(at1, in1))
    {
        cerr << "Erreur de lecture " << in1 << endl;
        return EXIT_FAILURE;
    }
    if (nb_files ==2 and !FromFile(at2, in2))
    {
        cerr << "Erreur de lecture " << in2 << endl;
        return EXIT_FAILURE;
    }

    switch(act)
    {
    case 0: //acc
        cout << "'" << acc << "' est accepté: " << Accept(at1, acc) << endl;
        atr = at1;
        break;
    case 1: //det
        atr = Determinize(at1);
        break;
    case 2: //cup
        atr =  Union(at1, at2);
        break;
    case 3: //cat
        atr =  Concat(at1, at2);
        break;
    case 4: //star
        atr =  Kleene(at1);
        break;
    case 5: //bar
        atr =  Complement(at1);
        break;
    case 6: //cap
        atr =  Intersection(at1, at2);
        break;
    case 7: //expr2aut
        atr =  ExpressionRationnelle2Automate(expr);
        break;
    case 8: //nop
        atr = at1;
        break;
    default:
        return EXIT_FAILURE;
    }

    // on affiche le résultat ou on l'écrit dans un fichier
    if(!toFile)
    {
        cout << atr;
    }

    else
    {
        if(graphMode)
        {
            ToGraph(atr, out + ".gv");
            system(("dot -Tpng " + out + ".gv -o " + out + ".png").c_str());
        }
        else
        {
            ofstream f((out + ".txt").c_str(), ios::trunc);
            if(f.fail())
                return EXIT_FAILURE;
            f << atr;
        }
    }

    return EXIT_SUCCESS;
}



